%def bindiv(result="", second="", wide="", suffix="", rem="0", ext="cdq"):
/*
 * 32-bit binary div/rem operation.  Handles special case of op1=-1.
 */
    /* div/rem vAA, vBB, vCC */
    movzbq  2(rPC), %rax                    # rax <- BB
    movzbq  3(rPC), %rcx                    # rcx <- CC
    .if $wide
    GET_WIDE_VREG %rax, %rax                # eax <- vBB
    GET_WIDE_VREG $second, %rcx             # ecx <- vCC
    .else
    GET_VREG %eax, %rax                     # eax <- vBB
    GET_VREG $second, %rcx                  # ecx <- vCC
    .endif
    test${suffix}   $second, $second
    jz      common_errDivideByZero
    cmp${suffix}  $$-1, $second
    je      2f
    $ext                                    # rdx:rax <- sign-extended of rax
    idiv${suffix}   $second
1:
    .if $wide
    SET_WIDE_VREG $result, rINSTq           # eax <- vBB
    .else
    SET_VREG $result, rINSTq                # eax <- vBB
    .endif
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
    .if $rem
    xor${suffix} $result, $result
    .else
    neg${suffix} $result
    .endif
    jmp     1b

%def bindiv2addr(result="", second="", wide="", suffix="", rem="0", ext="cdq"):
/*
 * 32-bit binary div/rem operation.  Handles special case of op1=-1.
 */
    /* div/rem/2addr vA, vB */
    movl    rINST, %ecx                     # rcx <- BA
    sarl    $$4, %ecx                       # rcx <- B
    andb    $$0xf, rINSTbl                  # rINST <- A
    .if $wide
    GET_WIDE_VREG %rax, rINSTq              # eax <- vA
    GET_WIDE_VREG $second, %rcx             # ecx <- vB
    .else
    GET_VREG %eax, rINSTq                   # eax <- vA
    GET_VREG $second, %rcx                  # ecx <- vB
    .endif
    test${suffix}   $second, $second
    jz      common_errDivideByZero
    cmp${suffix}  $$-1, $second
    je      2f
    $ext                                    # rdx:rax <- sign-extended of rax
    idiv${suffix}   $second
1:
    .if $wide
    SET_WIDE_VREG $result, rINSTq           # vA <- result
    .else
    SET_VREG $result, rINSTq                # vA <- result
    .endif
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
2:
    .if $rem
    xor${suffix} $result, $result
    .else
    neg${suffix} $result
    .endif
    jmp     1b

%def bindivLit16(result="", rem="0"):
/*
 * 32-bit binary div/rem operation.  Handles special case of op1=-1.
 */
    /* div/rem/lit16 vA, vB, #+CCCC */
    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
    movl    rINST, %eax                     # rax <- 000000BA
    sarl    $$4, %eax                       # eax <- B
    GET_VREG %eax, %rax                     # eax <- vB
    movswl  2(rPC), %ecx                    # ecx <- ssssCCCC
    andb    $$0xf, rINSTbl                  # rINST <- A
    testl   %ecx, %ecx
    jz      common_errDivideByZero
    cmpl    $$-1, %ecx
    je      2f
    cdq                                     # rax <- sign-extended of eax
    idivl   %ecx
1:
    SET_VREG $result, rINSTq                # vA <- result
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
    .if $rem
    xorl    $result, $result
    .else
    negl    $result
    .endif
    jmp     1b

%def bindivLit8(result="", rem="0"):
/*
 * 32-bit div/rem "lit8" binary operation.  Handles special case of
 * op0=minint & op1=-1
 */
    /* div/rem/lit8 vAA, vBB, #+CC */
    movzbq  2(rPC), %rax                    # eax <- BB
    movsbl  3(rPC), %ecx                    # ecx <- ssssssCC
    GET_VREG  %eax, %rax                    # eax <- rBB
    testl   %ecx, %ecx
    je      common_errDivideByZero
    cmpl    $$-1, %ecx
    je      2f
    cdq                                     # rax <- sign-extended of eax
    idivl   %ecx
1:
    SET_VREG $result, rINSTq                # vA <- result
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
2:
    .if $rem
    xorl    $result, $result
    .else
    negl    $result
    .endif
    jmp     1b

%def binop(result="%eax", instr=""):
/*
 * Generic 32-bit binary operation.  Provide an "instr" line that
 * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
 * This could be an x86 instruction or a function call.  (If the result
 * comes back in a register other than eax, you can override "result".)
 *
 * For: add-int, sub-int, and-int, or-int,
 *      xor-int, shl-int, shr-int, ushr-int
 */
    /* binop vAA, vBB, vCC */
    movzbq  2(rPC), %rax                    # rax <- BB
    movzbq  3(rPC), %rcx                    # rcx <- CC
    GET_VREG %eax, %rax                     # eax <- vBB
    $instr VREG_ADDRESS(%rcx),%eax
    SET_VREG $result, rINSTq
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

%def binop1(wide="0", instr=""):
/*
 * Generic 32-bit binary operation in which both operands loaded to
 * registers (op0 in eax, op1 in ecx).
 */
    /* binop vAA, vBB, vCC */
    movzbq  2(rPC), %rax                    # eax <- BB
    movzbq  3(rPC), %rcx                    # ecx <- CC
    GET_VREG %ecx, %rcx                     # eax <- vCC
    .if $wide
    GET_WIDE_VREG %rax, %rax                # rax <- vBB
    $instr                                  # ex: addl    %ecx,%eax
    SET_WIDE_VREG %rax, rINSTq
    .else
    GET_VREG %eax, %rax                     # eax <- vBB
    $instr                                  # ex: addl    %ecx,%eax
    SET_VREG %eax, rINSTq
    .endif
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

%def binop2addr(result="%eax", instr=""):
/*
 * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
 * that specifies an instruction that performs "result = r0 op r1".
 * This could be an instruction or a function call.
 *
 * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
 *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
 *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
 *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
 */
    /* binop/2addr vA, vB */
    movl    rINST, %ecx                     # rcx <- A+
    sarl    $$4, rINST                      # rINST <- B
    andb    $$0xf, %cl                      # ecx <- A
    GET_VREG %eax, rINSTq                   # eax <- vB
    $instr %eax, VREG_ADDRESS(%rcx)
    CLEAR_REF %rcx
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def binopLit16(result="%eax", instr=""):
/*
 * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
 * that specifies an instruction that performs "result = eax op ecx".
 * This could be an x86 instruction or a function call.  (If the result
 * comes back in a register other than eax, you can override "result".)
 *
 * For: add-int/lit16, rsub-int,
 *      and-int/lit16, or-int/lit16, xor-int/lit16
 */
    /* binop/lit16 vA, vB, #+CCCC */
    movl    rINST, %eax                     # rax <- 000000BA
    sarl    $$4, %eax                       # eax <- B
    GET_VREG %eax, %rax                     # eax <- vB
    andb    $$0xf, rINSTbl                  # rINST <- A
    movswl  2(rPC), %ecx                    # ecx <- ssssCCCC
    $instr                                  # for example: addl %ecx, %eax
    SET_VREG $result, rINSTq
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

%def binopLit8(result="%eax", instr=""):
/*
 * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
 * that specifies an instruction that performs "result = eax op ecx".
 * This could be an x86 instruction or a function call.  (If the result
 * comes back in a register other than r0, you can override "result".)
 *
 * For: add-int/lit8, rsub-int/lit8
 *      and-int/lit8, or-int/lit8, xor-int/lit8,
 *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
 */
    /* binop/lit8 vAA, vBB, #+CC */
    movzbq  2(rPC), %rax                    # rax <- BB
    movsbl  3(rPC), %ecx                    # rcx <- ssssssCC
    GET_VREG %eax, %rax                     # eax <- rBB
    $instr                                  # ex: addl %ecx,%eax
    SET_VREG $result, rINSTq
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

%def binopWide(instr=""):
/*
 * Generic 64-bit binary operation.
 */
    /* binop vAA, vBB, vCC */
    movzbq  2(rPC), %rax                    # eax <- BB
    movzbq  3(rPC), %rcx                    # ecx <- CC
    GET_WIDE_VREG %rax, %rax                # rax <- v[BB]
    $instr VREG_ADDRESS(%rcx),%rax
    SET_WIDE_VREG %rax, rINSTq              # v[AA] <- rax
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

%def binopWide2addr(instr=""):
/*
 * Generic 64-bit binary operation.
 */
    /* binop/2addr vA, vB */
    movl    rINST, %ecx                     # rcx <- A+
    sarl    $$4, rINST                      # rINST <- B
    andb    $$0xf, %cl                      # ecx <- A
    GET_WIDE_VREG %rax, rINSTq              # rax <- vB
    $instr %rax,VREG_ADDRESS(%rcx)
    CLEAR_WIDE_REF %rcx
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def cvtfp_int(fp_suffix="", i_suffix="", max_const="", result_reg="", wide=""):
/* On fp to int conversions, Java requires that
 * if the result > maxint, it should be clamped to maxint.  If it is less
 * than minint, it should be clamped to minint.  If it is a nan, the result
 * should be zero.  Further, the rounding mode is to truncate.
 */
    /* float/double to int/long vA, vB */
    movl    rINST, %ecx                     # rcx <- A+
    sarl    $$4, rINST                      # rINST <- B
    andb    $$0xf, %cl                      # ecx <- A
    GET_VREG_XMM${fp_suffix} %xmm0, rINSTq
    mov${i_suffix}  ${max_const}, ${result_reg}
    cvtsi2s${fp_suffix}${i_suffix} ${result_reg}, %xmm1
    comis${fp_suffix}    %xmm1, %xmm0
    jae     1f
    jp      2f
    cvtts${fp_suffix}2si${i_suffix}  %xmm0, ${result_reg}
    jmp     1f
2:
    xor${i_suffix}    ${result_reg}, ${result_reg}
1:
    .if $wide
    SET_WIDE_VREG ${result_reg}, %rcx
    .else
    SET_VREG ${result_reg}, %rcx
    .endif
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def shop2addr(wide="0", instr=""):
/*
 * Generic 32-bit "shift/2addr" operation.
 */
    /* shift/2addr vA, vB */
    movl    rINST, %ecx                     # ecx <- BA
    sarl    $$4, %ecx                       # ecx <- B
    GET_VREG %ecx, %rcx                     # ecx <- vBB
    andb    $$0xf, rINSTbl                  # rINST <- A
    .if $wide
    GET_WIDE_VREG %rax, rINSTq              # rax <- vAA
    $instr                                  # ex: sarl %cl, %eax
    SET_WIDE_VREG %rax, rINSTq
    .else
    GET_VREG %eax, rINSTq                   # eax <- vAA
    $instr                                  # ex: sarl %cl, %eax
    SET_VREG %eax, rINSTq
    .endif
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def unop(preinstr="", instr="", wide="0"):
/*
 * Generic 32/64-bit unary operation.  Provide an "instr" line that
 * specifies an instruction that performs "result = op eax".
 */
    /* unop vA, vB */
    movl    rINST, %ecx                     # rcx <- A+
    sarl    $$4,rINST                       # rINST <- B
    .if ${wide}
    GET_WIDE_VREG %rax, rINSTq              # rax <- vB
    .else
    GET_VREG %eax, rINSTq                   # eax <- vB
    .endif
    andb    $$0xf,%cl                       # ecx <- A
$preinstr
$instr
    .if ${wide}
    SET_WIDE_VREG %rax, %rcx
    .else
    SET_VREG %eax, %rcx
    .endif
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def op_add_int():
%  binop(instr="addl")

%def op_add_int_2addr():
%  binop2addr(instr="addl")

%def op_add_int_lit16():
%  binopLit16(instr="addl    %ecx, %eax")

%def op_add_int_lit8():
%  binopLit8(instr="addl    %ecx, %eax")

%def op_add_long():
%  binopWide(instr="addq")

%def op_add_long_2addr():
%  binopWide2addr(instr="addq")

%def op_and_int():
%  binop(instr="andl")

%def op_and_int_2addr():
%  binop2addr(instr="andl")

%def op_and_int_lit16():
%  binopLit16(instr="andl    %ecx, %eax")

%def op_and_int_lit8():
%  binopLit8(instr="andl    %ecx, %eax")

%def op_and_long():
%  binopWide(instr="andq")

%def op_and_long_2addr():
%  binopWide2addr(instr="andq")

%def op_cmp_long():
/*
 * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
 * register based on the results of the comparison.
 */
    /* cmp-long vAA, vBB, vCC */
    movzbq  2(rPC), %rdx                    # edx <- BB
    movzbq  3(rPC), %rcx                    # ecx <- CC
    GET_WIDE_VREG %rdx, %rdx                # rdx <- v[BB]
    xorl    %eax, %eax
    xorl    %edi, %edi
    addb    $$1, %al
    movl    $$-1, %esi
    cmpq    VREG_ADDRESS(%rcx), %rdx
    cmovl   %esi, %edi
    cmovg   %eax, %edi
    SET_VREG %edi, rINSTq
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 2

%def op_div_int():
%  bindiv(result="%eax", second="%ecx", wide="0", suffix="l")

%def op_div_int_2addr():
%  bindiv2addr(result="%eax", second="%ecx", wide="0", suffix="l")

%def op_div_int_lit16():
%  bindivLit16(result="%eax")

%def op_div_int_lit8():
%  bindivLit8(result="%eax")

%def op_div_long():
%  bindiv(result="%rax", second="%rcx", wide="1", suffix="q", ext="cqo")

%def op_div_long_2addr():
%  bindiv2addr(result="%rax", second="%rcx", wide="1", suffix="q", ext="cqo")

%def op_int_to_byte():
%  unop(instr="movsbl  %al, %eax")

%def op_int_to_char():
%  unop(instr="movzwl  %ax,%eax")

%def op_int_to_long():
    /* int to long vA, vB */
    movzbq  rINSTbl, %rax                   # rax <- +A
    sarl    $$4, %eax                       # eax <- B
    andb    $$0xf, rINSTbl                  # rINST <- A
    movslq  VREG_ADDRESS(%rax), %rax
    SET_WIDE_VREG %rax, rINSTq              # v[A] <- %rax
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1


%def op_int_to_short():
%  unop(instr="movswl %ax, %eax")

%def op_long_to_int():
/* we ignore the high word, making this equivalent to a 32-bit reg move */
%  op_move()

%def op_mul_int():
%  binop(instr="imull")

%def op_mul_int_2addr():
    /* mul vA, vB */
    movl    rINST, %ecx                     # rcx <- A+
    sarl    $$4, rINST                      # rINST <- B
    andb    $$0xf, %cl                      # ecx <- A
    GET_VREG %eax, %rcx                     # eax <- vA
    imull   (rFP,rINSTq,4), %eax
    SET_VREG %eax, %rcx
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def op_mul_int_lit16():
%  binopLit16(instr="imull   %ecx, %eax")

%def op_mul_int_lit8():
%  binopLit8(instr="imull   %ecx, %eax")

%def op_mul_long():
%  binopWide(instr="imulq")

%def op_mul_long_2addr():
    /* mul vA, vB */
    movl    rINST, %ecx                     # rcx <- A+
    sarl    $$4, rINST                      # rINST <- B
    andb    $$0xf, %cl                      # ecx <- A
    GET_WIDE_VREG %rax, %rcx                # rax <- vA
    imulq   (rFP,rINSTq,4), %rax
    SET_WIDE_VREG %rax, %rcx
    ADVANCE_PC_FETCH_AND_GOTO_NEXT 1

%def op_neg_int():
%  unop(instr="    negl    %eax")

%def op_neg_long():
%  unop(instr="    negq    %rax", wide="1")

%def op_not_int():
%  unop(instr="    notl    %eax")

%def op_not_long():
%  unop(instr="    notq    %rax", wide="1")

%def op_or_int():
%  binop(instr="orl")

%def op_or_int_2addr():
%  binop2addr(instr="orl")

%def op_or_int_lit16():
%  binopLit16(instr="orl     %ecx, %eax")

%def op_or_int_lit8():
%  binopLit8(instr="orl     %ecx, %eax")

%def op_or_long():
%  binopWide(instr="orq")

%def op_or_long_2addr():
%  binopWide2addr(instr="orq")

%def op_rem_int():
%  bindiv(result="%edx", second="%ecx", wide="0", suffix="l", rem="1")

%def op_rem_int_2addr():
%  bindiv2addr(result="%edx", second="%ecx", wide="0", suffix="l", rem="1")

%def op_rem_int_lit16():
%  bindivLit16(result="%edx", rem="1")

%def op_rem_int_lit8():
%  bindivLit8(result="%edx", rem="1")

%def op_rem_long():
%  bindiv(result="%rdx", second="%rcx", wide="1", suffix="q", ext="cqo", rem="1")

%def op_rem_long_2addr():
%  bindiv2addr(result="%rdx", second="%rcx", wide="1", suffix="q", rem="1", ext="cqo")

%def op_rsub_int():
/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
%  binopLit16(instr="subl    %eax, %ecx", result="%ecx")

%def op_rsub_int_lit8():
%  binopLit8(instr="subl    %eax, %ecx", result="%ecx")

%def op_shl_int():
%  binop1(instr="sall    %cl, %eax")

%def op_shl_int_2addr():
%  shop2addr(instr="sall    %cl, %eax")

%def op_shl_int_lit8():
%  binopLit8(instr="sall    %cl, %eax")

%def op_shl_long():
%  binop1(instr="salq    %cl, %rax", wide="1")

%def op_shl_long_2addr():
%  shop2addr(instr="salq    %cl, %rax", wide="1")

%def op_shr_int():
%  binop1(instr="sarl    %cl, %eax")

%def op_shr_int_2addr():
%  shop2addr(instr="sarl    %cl, %eax")

%def op_shr_int_lit8():
%  binopLit8(instr="sarl    %cl, %eax")

%def op_shr_long():
%  binop1(instr="sarq    %cl, %rax", wide="1")

%def op_shr_long_2addr():
%  shop2addr(instr="sarq    %cl, %rax", wide="1")

%def op_sub_int():
%  binop(instr="subl")

%def op_sub_int_2addr():
%  binop2addr(instr="subl")

%def op_sub_long():
%  binopWide(instr="subq")

%def op_sub_long_2addr():
%  binopWide2addr(instr="subq")

%def op_ushr_int():
%  binop1(instr="shrl    %cl, %eax")

%def op_ushr_int_2addr():
%  shop2addr(instr="shrl    %cl, %eax")

%def op_ushr_int_lit8():
%  binopLit8(instr="shrl    %cl, %eax")

%def op_ushr_long():
%  binop1(instr="shrq    %cl, %rax", wide="1")

%def op_ushr_long_2addr():
%  shop2addr(instr="shrq    %cl, %rax", wide="1")

%def op_xor_int():
%  binop(instr="xorl")

%def op_xor_int_2addr():
%  binop2addr(instr="xorl")

%def op_xor_int_lit16():
%  binopLit16(instr="xorl    %ecx, %eax")

%def op_xor_int_lit8():
%  binopLit8(instr="xorl    %ecx, %eax")

%def op_xor_long():
%  binopWide(instr="xorq")

%def op_xor_long_2addr():
%  binopWide2addr(instr="xorq")
